using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Waymex.Gis.Coordinates;
using Waymex.Gis.Ellipsoids;
using Waymex.Gis.Projections;

namespace Waymex.Gis
{
    /// <summary>
    /// Represents a single record of OSTN02/OSTM02 data.
    /// </summary>
    internal class OSTN02Record
    {

        //member variables
        Osgb36GeoidDatum _geoidDatum = 0;
        //double[,] _seSnSg = new double[4, 2];
        
        private double _sg = 0.0;
        private double _se = 0.0;
        private double _sn = 0.0;

        private double _easting = 0.0;
        private double _northing = 0.0;
        private double _height = 0.0;

        public double Easting
        {
            get { return _easting; }
        }
        public double Northing
        {
            get { return _northing; }
        }

        #region Properties
        /// <summary>
        /// ODNT02 Height. Check
        /// </summary>
        public double Height
        {
            get { return _height; }
        }

        /// <summary>
        /// Returns the easting shift of the ETRS89 coordinate.
        /// </summary>
        public double Se
        {
            get { return _se; }
        }
        /// <summary>
        /// Returns the northing shift of the ETRS89 coordinate.
        /// </summary>
        public double Sn
        {
            get { return _sn; }
        }
        /// <summary>
        /// Returns geiod height of the cell selected by easting and northing passed in to the constructor.
        /// </summary>
        public double Sg
        {
            get { return _sg; }
        }

        ///// <summary>
        ///// Returns easting shift of bottom left corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Se0
        //{
        //    get { return _se0; }
        //}
        ///// <summary>
        ///// Returns northing shift of bottom left corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Sn0
        //{
        //    get { return _sn0; }
        //}
        ///// <summary>
        ///// Returns easting shift of bottom right corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Se1
        //{
        //    get { return _se1; }
        //}
        ///// <summary>
        ///// Returns northing shift of bottom right corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Sn1
        //{
        //    get { return _sn1; }
        //}
        ///// <summary>
        ///// Returns easting shift of top right corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Se2
        //{
        //    get { return _se2; }
        //}
        ///// <summary>
        ///// Returns northing shift of top right corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Sn2
        //{
        //    get { return _sn2; }
        //}
        ///// <summary>
        ///// Returns easting shift of top left corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Se3
        //{
        //    get { return _se3; }
        //}
        ///// <summary>
        ///// Returns easting shift of top left corner of the cell selected by easting and northing passed in to the constructor.
        ///// </summary>
        //public double Sn3
        //{
        //    get { return _sn3; }
        //}

        /// <summary>
        /// Returns the regional height Datum in use.
        /// </summary>
        public Osgb36GeoidDatum RegionGeoidDatum
        {
            get { return (_geoidDatum); }
        }

        #endregion

        public OSTN02Record(LatitudeLongitudeCoordinates coordinates, string dataFileName)
        {
            LoadRecord(Convert.ToEastingNorthing(new Grs80Ellipsoid(), new BngProjection(), coordinates), coordinates.ElipsoidalHeight, dataFileName);
        }
        /// <summary>
        /// Constructor. Requires the ETRS89 coordinates to transform and the filename (including the path) 
        /// to the OSTN02/OSTM02 GB data file.
        /// </summary>
        /// <param name="coordinates"></param>
        /// <param name="height"></param>
        /// <param name="dataFileName">Filename (including the path) to the OSTN02/OSTM02 GB data file.
        /// </param>
        public OSTN02Record(EastingNorthingCoordinates coordinates, double height, string dataFileName)
        {
            LoadRecord(coordinates, height, dataFileName);
        }
        private void LoadRecord(EastingNorthingCoordinates coordinates, double height, string dataFileName)
        {
            //string[] record = new string[4] { String.Empty, String.Empty, String.Empty, String.Empty }; //need to get four records in one pass of the file;
            TextReader tr = new StreamReader(dataFileName);
            List<int> recordNumbers = new List<int>();
            string[] records = new string[4];

            double _se0 = 0.0;
            double _se1 = 0.0;
            double _se2 = 0.0;
            double _se3 = 0.0;

            double _sn0 = 0.0;
            double _sn1 = 0.0;
            double _sn2 = 0.0;
            double _sn3 = 0.0;

            double _sg0 = 0.0;
            double _sg1 = 0.0;
            double _sg2 = 0.0;
            double _sg3 = 0.0;

            //determine record numbers
            int eastIndex = (int)(coordinates.Easting / 1000.0);
            int northIndex = (int)(coordinates.Northing / 1000.0);

            double x0 = eastIndex * 1000;
            double y0 = northIndex * 1000;

            //work out the four records
            recordNumbers.Add(CalculateRecordNumber(eastIndex, northIndex));
            recordNumbers.Add(CalculateRecordNumber(eastIndex + 1, northIndex));
            recordNumbers.Add(CalculateRecordNumber(eastIndex + 1, northIndex + 1));
            recordNumbers.Add(CalculateRecordNumber(eastIndex, northIndex + 1));

            //get records from the data file
            int recordsFound = 0;
            while (recordsFound < 4)
            {
                string csvRecord = tr.ReadLine();
                for (int index = 0; index < 4; index++)
                {
                    if (csvRecord.StartsWith(recordNumbers[index].ToString().Trim() + ","))
                    {
                        //dont use add as we need to keep these in same order as record numbers
                        records[index] = csvRecord;
                        recordsFound++;
                    }
                }
            }

            //populate the properties
            string[] fields0 = records[0].Split(",".ToCharArray());
            string[] fields1 = records[1].Split(",".ToCharArray());
            string[] fields2 = records[2].Split(",".ToCharArray());
            string[] fields3 = records[3].Split(",".ToCharArray());

            double se0 = System.Convert.ToDouble(fields0[3]);
            double se1 = System.Convert.ToDouble(fields1[3]);
            double se2 = System.Convert.ToDouble(fields2[3]);
            double se3 = System.Convert.ToDouble(fields3[3]);

            double sn0 = System.Convert.ToDouble(fields0[4]);
            double sn1 = System.Convert.ToDouble(fields1[4]);
            double sn2 = System.Convert.ToDouble(fields2[4]);
            double sn3 = System.Convert.ToDouble(fields3[4]);

            double sg0 = System.Convert.ToDouble(fields0[5]);
            double sg1 = System.Convert.ToDouble(fields1[5]);
            double sg2 = System.Convert.ToDouble(fields2[5]);
            double sg3 = System.Convert.ToDouble(fields3[5]);

            double dx = coordinates.Easting - x0;
            double dy = coordinates.Northing - y0;

            double t = dx / 1000.0;
            double u = dy / 1000.0;

            _se = (1 - t) * (1 - u) * se0 + t * (1 - u) * se1 + t * u * se2 + (1 - t) * u * se3;
            _sn = (1 - t) * (1 - u) * sn0 + t * (1 - u) * sn1 + t * u * sn2 + (1 - t) * u * sn3;
            _sg = (1 - t) * (1 - u) * sg0 + t * (1 - u) * sg1 + t * u * sg2 + (1 - t) * u * sg3;

            _geoidDatum = (Osgb36GeoidDatum)System.Convert.ToInt32(fields0[6]);

            _easting = coordinates.Easting + _se;
            _northing = coordinates.Northing + _sn;
            _height = height - _sg;
        }
        /// <summary>
        /// Calculates a data file record number.
        /// </summary>
        /// <param name="eastIndex"></param>
        /// <param name="northIndex"></param>
        /// <returns></returns>
        private int CalculateRecordNumber(int eastIndex, int northIndex)
        {
            return eastIndex + (northIndex * 701) + 1;
        }
    }
}
